using System;
using System.Text;
using System.Runtime.InteropServices;

using SqInterface.Utils;
using SqInterface.InteropServices;

namespace SqInterface
{
	// definitions used by the end user.
	public delegate int  SqMethodDelegate( SqVM vm );

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int SqExReaderFunction(object data);

    public delegate int  SqReaderDelegate(SqVM vm, object data, int i1);
	public delegate void SqCompilerErrorDelegate( SqVM vm, string Desc, int Line, int Column );
	public delegate void SqExPrinterFunction( StringBuilder buffer );

	public class SqUserMethod
	{
		private SqMethodDelegate _usrDelegate;
		private MulticastDelegate _apiDelegate;

		internal SqUserMethod( SqMethodDelegate usrDelegate )
		{
			_usrDelegate = usrDelegate;
			_apiDelegate = SqAPI.CreateCompatibleDelegate(new SqAPIMethodDelegate(ThisMethodDelegate));
		}
		private int ThisMethodDelegate( IntPtr vmref )
		{
			return _usrDelegate( Squirrel.GetVM( vmref ) );
		}
		internal MulticastDelegate APIDelegate
		{
			get { return _apiDelegate; }
		}
		public SqMethodDelegate UsrDelegate
		{
			get { return _usrDelegate; }
		}		
	}

    public class SqExReaderObject
    {
        SqExReaderFunction usrFn;
        SqAPIExReaderFunction apiFn;
        IntPtr apiPt;
        internal object data;

        public SqExReaderObject(SqExReaderFunction readFn, object data)
        {
            this.apiFn = new SqAPIExReaderFunction(ExApiReaderDelegate);
            this.apiPt = Marshal.GetFunctionPointerForDelegate(apiFn);
            this.usrFn = readFn;
            this.data = data;
        }

        private int ExApiReaderDelegate(IntPtr data)
        {
            int r = usrFn(this.data);
            return r;
        }

        internal SqAPIExReaderFunction ApiCallbackFunction
        {
            get { return apiFn; }
        }

        internal IntPtr ApiIntPtr
        {
            get { return apiPt; }
        }
    }

    public class SqExPrinterObject
    {
        SqApiCompatiblePrintFunction apiFn;
        IntPtr apiPt;

        public SqExPrinterObject()
        {
            apiFn = new SqApiCompatiblePrintFunction(ConsolePrintFunction);
            apiPt = Marshal.GetFunctionPointerForDelegate(apiFn);
        }
        public SqExPrinterObject(SqApiCompatiblePrintFunction printFn)
        {
            apiFn = printFn;
            apiPt = Marshal.GetFunctionPointerForDelegate(apiFn);
        }

        public SqApiCompatiblePrintFunction PrintFunction
        {
            get { return apiFn; }
            set { apiFn = value; apiPt = Marshal.GetFunctionPointerForDelegate(apiFn); }
        }

        internal IntPtr ApiIntPtr
        {
            get { return apiPt; }
        }

        private static void ConsolePrintFunction(StringBuilder buffer)
        {
            Console.Write(buffer.ToString());
        }
    }

	/// <summary>
	/// Summary description for Class1.
	/// </summary>
	public class SqVM
	{
		#region Members
		private IntPtr _vm;
		private bool toplevel=true;
		private int result;

        private SqExPrinterObject prnFunction;
        private SqExPrinterObject errFunction;

        #endregion

		#region Constructor and Destructors

		internal SqVM( int HintStackSize )
		{
			_vm = SqAPI.SqOpen( HintStackSize );
            BootstrapPrinting();
        }

		internal SqVM( IntPtr vm )
		{
			_vm = vm;
			toplevel = false;
            BootstrapPrinting();
		}

		~SqVM()
		{
			if ( toplevel )
				SqAPI.SqClose( _vm );
		}

		#endregion

        private void BootstrapPrinting()
        {
            prnFunction = new SqExPrinterObject(new SqApiCompatiblePrintFunction(ApiCompatiblePrintFunction));
            errFunction = new SqExPrinterObject(new SqApiCompatiblePrintFunction(ApiCompatibleErrorFunction));
            int result = SqAPI.SqSetCompatiblePrintFunctions(_vm, prnFunction.ApiIntPtr, errFunction.ApiIntPtr);
        }


        public int SetRootInteger(string name, int value)
        {
            int tiptop = GetTop();
            PushRootTable();
            int top = GetTop();
            PushString(name);
            PushInteger(value);
            int result = RawSet(-3);

            while (GetTop() > tiptop)
                Pop(1);

            return result;
        }
        public int GetRootInteger(string name, ref int value)
        {
            int tiptop = GetTop();
            PushRootTable();
            PushString(name);
            int result = RawGet(-2);
            int top = GetTop();
            result = GetInteger(-1, ref value);

            while (GetTop() > tiptop)
                Pop(1);

            value = SqAPI.SqSuccess(result) ? value : 0;

            return result;
        }
        public int GetRootPointer(string name, ref IntPtr value)
        {
            int tiptop = GetTop();
            PushRootTable();
            PushString(name);
            int result = RawGet(-2);
            int top = GetTop();
            result = GetUserPointer(-1, ref value);

            while (GetTop() > tiptop)
                Pop(1);

            return result;
        }

        private void ApiCompatiblePrintFunction(StringBuilder buffer)
        {
            Console.WriteLine("PRINT:{0}", buffer);
        }
        private void ApiCompatibleErrorFunction([Out] StringBuilder buffer)
        {
            Console.WriteLine("ERROR:{0}", buffer);
        }

        public SqExPrinterObject PrintFunctionObject
        {
            get { return prnFunction; }
        }
        public SqExPrinterObject ErrorFunctionObject
        {
            get { return errFunction; }
        }

		#region Conveniance Properties and Methods
		public IntPtr VM
		{
			get { return _vm; }
		}
		#endregion

		#region Virtual Machine

		public IntPtr GetForeignPointer( )
		{
			return SqAPI.SqGetForeignPointer( _vm );
		}

		public int GetVMState( )
		{
			return SqAPI.SqGetVMState( _vm );
		}

		public void Move( SqVM Target, int Idx )
		{
			SqAPI.SqMove( Target.VM, _vm, Idx );
		}

		public SqVM NewThread( int initialStackSize )
		{
			return new SqVM( SqAPI.SqNewThread( _vm, initialStackSize ) );
		}

		public void PushRegistryTable()
		{
			SqAPI.SqPushRegisteryTable( _vm );
		}

		public void PushRootTable()
		{
			SqAPI.SqPushRootTable( _vm );
		}

		public void SetErrorHandler()
		{
			SqAPI.SqSetErrorHandler( _vm );
		}

		public void SetForeignPtr( IntPtr up )
		{
			SqAPI.SqSetForeignPtr( _vm, up );
		}

		public void SetPrintFunc( IntPtr prnFn, IntPtr errFn )
		{
			SqAPI.SqSetPrintFunc( _vm, prnFn, errFn );
		}

		public void SetRootTable( )
		{
			SqAPI.SqSetRootTable( _vm );
		}

		public int SuspendVM()
		{
			return SqAPI.SqSuspendVM( _vm );
		}

		public int WakeUpVM(bool ResumedRet, bool RetVal)
		{
			return SqAPI.SqWakeUpVM( _vm, ResumedRet, RetVal );
		}

		#endregion

		#region Compiler Services

        public void Compile(SqExReaderObject ReaderObject, string SourceName, bool RaiseError)
        {
            SqAPI.SqCompileReader(_vm, ReaderObject.ApiCallbackFunction, IntPtr.Zero, SourceName, RaiseError);
        }
		public void Compile( SqExReaderFunction ReaderFunc, object data, string SourceName, bool RaiseError )
		{
            SqExReaderObject readerObj = new SqExReaderObject(ReaderFunc, data);
            try
            {
                Compile(readerObj, SourceName, RaiseError);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
		}
		public void Compile( string buffer, string SourceName, bool RaiseError )
		{
			SqAPI.SqCompileBuffer( _vm, buffer, buffer.Length, SourceName, RaiseError );
		}

		public void EnableDebugInfo( bool debugInfo )
		{
			SqAPI.SqEnableDebugInfo( _vm, debugInfo );
		}

		public SqCompilerErrorDelegate SetCompilerHandler( SqCompilerErrorDelegate func )
		{
			//SqCompilerErrorDelegate temp = _user_delegate_compile;
			//_user_delegate_compile = func;
			return null;
		}

        /*
		private void ConstructReaderDelegate( )
		{
            _api_delegate_reader = new SqAPIExReaderFunction(ThisReaderDelegate);
//            _user_data_read = new SqAPIReaderDelegate(ThisReaderDelegate);
            _that_delegate_exreader = Marshal.GetFunctionPointerForDelegate((Delegate)_api_delegate_reader);
//			_this_delegate_read = 
//				DelegateGenerator.CreateDelegate( new SqAPIReaderDelegate(ThisReaderDelegate) , 
//				System.Runtime.InteropServices.CallingConvention.Cdecl );
		}
		private int ThisReaderDelegate(IntPtr data)
		{
            return _api_delegate_reader(data);
			// return _user_delegate_read(this, _user_data_read);
		}

		private void ConstructCompilerDelegate()
		{
            _api_delegate_compiler_error = new SqAPICompilerErrorDelegate(ThisCompilerErrorDelegate);
//            _user_delegate_compile = new SqCompilerErrorDelegate(ThisCompilerErrorDelegate);
            _that_delegate_compiler = Marshal.GetFunctionPointerForDelegate((Delegate)_api_delegate_compiler_error);
//            _this_delegate_compile = 
//				DelegateGenerator.CreateDelegate( new SqAPICompilerErrorDelegate(ThisCompilerErrorDelegate) , 
//				System.Runtime.InteropServices.CallingConvention.Cdecl );
			SqAPI.SqSetCompilerErrorHandler( _vm, _that_delegate_compiler );
//			_user_delegate_compile = new SqCompilerErrorDelegate( ThisDefaultCompilerErrorDelegate );
		}
		private void ThisCompilerErrorDelegate( IntPtr vmref, string desc, int Line, int Column  )
		{
			_user_delegate_compile( this, desc, Line, Column );
		}
		private void ThisDefaultCompilerErrorDelegate( SqVM vm, string desc, int Line, int Column )
		{
			// do nothing
		}
         * */

		#endregion

		#region Stack Operations

		public int Cmp()
		{
			return SqAPI.SqCmp( _vm );
		}

		public int GetTop()
		{
			return SqAPI.SqGetTop( _vm );
		}

		public void Pop( int Count )
		{
			SqAPI.SqPop( _vm, Count );
		}

		public void PopTop( )
		{
			SqAPI.SqPopTop( _vm );
		}

		public void Push( int Index )
		{
			SqAPI.SqPush( _vm, Index );
		}

		public void Remove( int Index )
		{
			SqAPI.SqRemove( _vm, Index );
		}

		public void ReserveStack( int Index )
		{
			SqAPI.SqReserveStack( _vm, Index );
		}

		public void SetTop( int Count )
		{
			SqAPI.SqSetTop( _vm, Count );
		}

		#endregion

		#region Object Creation and Handling

		public int CreateInstance( int Index )
		{
			return SqAPI.SqCreateInstance( _vm, Index );
		}

		public int GetBool( int Index, ref bool result )
		{
			result=false;
			return SqAPI.SqGetBool( _vm, Index, ref result );
		}

		public int GetClosureInfo( int Index, ref uint nparams, ref uint nfreevars )
		{
			return SqAPI.SqGetClosureInfo( _vm, Index, ref nparams, ref nfreevars );
		}

		public int GetFloat( int Index, ref float result )
		{
			result=0;
			return SqAPI.SqGetFloat( _vm, Index, ref result );
		}

		public int GetInstanceUp( int Index, ref IntPtr up, IntPtr TypeTag )
		{
			return SqAPI.SqGetInstanceUp( _vm, Index, ref up, TypeTag );
		}

		public int GetInteger( int Index, ref int result )
		{
			return SqAPI.SqGetInteger( _vm, Index, ref result );
		}

		public IntPtr GetScratchPad( int minsize )
		{
			return SqAPI.SqGetScratchpad( _vm, minsize );
		}

		public int GetSize( int Index )
		{
			return SqAPI.SqGetSize( _vm, Index );
		}

		public int GetString( int Index, out string result )
		{
            result = null; ;
            IntPtr ptr = IntPtr.Zero;
            int apiResult = SqAPI.SqGetString(_vm, Index, ref ptr);
            if (SqAPI.SqSuccess(apiResult))
                result = Marshal.PtrToStringAnsi(ptr);
            return apiResult;
		}

		public int GetThread( int Index, ref IntPtr vm )
		{
			return SqAPI.SqGetThread( _vm, Index, ref vm );
		}

		public int GetType( int Index )
		{
			return SqAPI.SqGetType( _vm, Index );
		}
		public string GetTypeName( int Index )
		{
			return SqObjectType.GetTypeName( GetType( Index ) );
		}

		#region Stack Type Identifiers
		public bool IsType( int Index, int SquirrelType )
		{
			return ( SqAPI.SqGetType( _vm, Index ) == SquirrelType );
		}
		public bool IsArray( int Index )
		{
			return IsType( Index, SqObjectType.OT_ARRAY );
		}
		public bool IsBool( int Index )
		{
			return IsType( Index, SqObjectType.OT_BOOL );
		}
		public bool IsClass( int Index )
		{
			return IsType( Index, SqObjectType.OT_CLASS );
		}
		public bool IsClosure( int Index )
		{
			return IsType( Index, SqObjectType.OT_CLOSURE );
		}
		public bool IsFloat( int Index )
		{
			return IsType( Index, SqObjectType.OT_FLOAT );
		}
		public bool IsGenerator( int Index )
		{
			return IsType( Index, SqObjectType.OT_GENERATOR );
		}
		public bool IsInstance( int Index )
		{
			return IsType( Index, SqObjectType.OT_INSTANCE );
		}
		public bool IsNativeClosure( int Index )
		{
			return IsType( Index, SqObjectType.OT_NATIVECLOSURE );
		}
		public bool IsNull( int Index )
		{
			return IsType( Index, SqObjectType.OT_NULL );
		}
		public bool IsString( int Index )
		{
			return IsType( Index, SqObjectType.OT_STRING );
		}
		public bool IsInteger( int Index )
		{
			return IsType( Index, SqObjectType.OT_INTEGER );
		}
		public bool IsTable( int Index )
		{
			return IsType( Index, SqObjectType.OT_TABLE );
		}
		public bool IsThread( int Index )
		{
			return IsType( Index, SqObjectType.OT_THREAD );
		}
		public bool IsUserData( int Index )
		{
			return IsType( Index, SqObjectType.OT_USERDATA );
		}
		public bool IsUserPointer( int Index )
		{
			return IsType( Index, SqObjectType.OT_USERPOINTER );
		}
		public bool IsWeakRef( int Index )
		{
			return IsType( Index, SqObjectType.OT_WEAKREF );
		}
		#endregion

		public int GetUserData( int Index, ref IntPtr UserPointer, ref IntPtr TypeTag )
		{
			return SqAPI.SqGetUserData( _vm, Index, ref UserPointer, ref TypeTag );
		}

		public int GetUserPointer( int Index, ref IntPtr up )
		{
			return SqAPI.SqGetUserPointer( _vm, Index, ref up );
		}

		public void NewArray()
		{
			SqAPI.SqNewArray( _vm );
		}

		public int NewClass()
		{
			return SqAPI.SqNewClass( _vm );
		}

		public SqUserMethod NewClosure( SqMethodDelegate userMethod, int nfreevars )
		{
			SqUserMethod mth = new SqUserMethod( userMethod );
			SqAPI.SqNewClosure( _vm, mth.APIDelegate, nfreevars );
			return mth;
		}

		public void NewTable( )
		{
			SqAPI.SqNewTable( _vm );
		}

		public IntPtr NewUserData( uint size )
		{
			return SqAPI.SqNewUserData( _vm, size );
		}

		public void PushBool( bool val )
		{
			SqAPI.SqPushBool( _vm, val );
		}

		public void PushFloat( float val )
		{
			SqAPI.SqPushFloat( _vm, val );
		}

		public void PushInteger( int val )
		{
			SqAPI.SqPushInteger( _vm, val );
		}

		public void PushString( string val )
		{
			SqAPI.SqPushString( _vm, val, val.Length );
		}

		public int Call( int paramCount, bool retValues, bool raiseError )
		{
			result = SqAPI.SqCall( _vm, paramCount, retValues, raiseError );
			return result;
		}

		#endregion

		#region Objects Manipulation

		public int ArrayAppend( int Index )
		{
			return SqAPI.SqArrayAppend( _vm, Index );
		}

		public int ArrayPop( int Index )
		{
			return SqAPI.SqArrayPop( _vm, Index );
		}

		public int ArrayResize( int Index, int newsize )
		{
			return SqAPI.SqArrayResize( _vm, Index, newsize );
		}

		public int ArrayReverse( int Index )
		{
			return SqAPI.SqArrayReverse( _vm, Index );
		}

		public int Clone( int Index )
		{
			return SqAPI.SqClone( _vm, Index );
		}

		public int CreateSlot( int Index )
		{
			return SqAPI.SqCreateSlot( _vm, Index );
		}

		public int DeleteSlot( int Index, bool pushval )
		{
			return SqAPI.SqDeleteSlot( _vm, Index, pushval );
		}

		public int Get( int Index )
		{
			return SqAPI.SqGet( _vm, Index );
		}

		public int GetAttributes( int Index )
		{
			return SqAPI.SqGetAttributes( _vm, Index );
		}

		public int GetClass( int Index )
		{
			return SqAPI.SqGetClass( _vm, Index );
		}

		public int GetDelegate( int Index )
		{
			return SqAPI.SqGetDelegate( _vm, Index );
		}

		public string GetFreeVariable( int Index, int nval )
		{
			return SqAPI.SqGetFreeVariable( _vm, Index, nval );
		}

		public int GetWeakRefVal( int Index )
		{
			return SqAPI.SqGetWeakRefVal( _vm, Index );
		}

		public int Next( int Index )
		{
			return SqAPI.SqNext( _vm, Index );
		}

		public int RawDeleteSlot( int Index, bool pushval )
		{
			return SqAPI.SqRawDeleteSlot( _vm, Index, pushval );
		}

		public int RawGet( int Index )
		{
			return SqAPI.SqRawGet( _vm, Index );
		}

		public int RawSet( int Index )
		{
			return SqAPI.SqRawSet( _vm, Index );
		}

		public int Set( int Index )
		{
			return SqAPI.SqSet( _vm, Index );
		}

		public int SetAttributes( int Index )
		{
			return SqAPI.SqSetAttributes( _vm, Index );
		}

		public int SetDelegate( int Index )
		{
			return SqAPI.SqSetDelegate( _vm, Index );
		}

		public int SetFreeVariable( int Index, int nval )
		{
			return SqAPI.SqSetFreeVariable( _vm, Index, nval );
		}

		public int WeakRef( int Index )
		{
			return SqAPI.SqWeakRef( _vm, Index );
		}

		#endregion

		#region Play Area
		void TestExec( string code )
		{
		}
		private void ThisReadDelegate()
		{
		}

		void ThisCompileErrorDelegate( IntPtr vmref, string Desc, int Line, int Column )
		{
		}
		#endregion

	}
}
